<?php


namespace EchoPHP\Http\Support;


use EchoPHP\File\UploadedFile;
use EchoPHP\Http\Cookie;
use InvalidArgumentException;

trait InteractsWithInput {

    protected $filter;

    /**
     * 获取get参数
     * @param string $name
     * @param null $default
     * @param array $filter
     * @return array|mixed|void|null
     */
    public function getGet($name = '', $default = null, $filter = []) {
        $data = $this->query->all();
        return $this->input($data, $name, $default, $filter);
    }

    /**
     * 获取post 参数
     * @param string $name
     * @param null $default
     * @param array $filter
     * @return array|mixed|void|null
     */
    public function getPost($name = '', $default = null, $filter = []) {
        $data = $this->request->all();
        return $this->input($data, $name, $default, $filter);
    }

    /**
     * 获取通过pathinfo传递的参数
     * @param string $name
     * @param null $default
     * @param array $filter
     * @return array|mixed|void|null
     */
    public function getAttribute($name = '', $default = null, $filter = []) {
        $data = $this->attributes->all();
        return $this->input($data, $name, $default, $filter);
    }

    /**
     * 从post、get、pathinfo中获取参数，如果有重名，按 post > get > pathinfo 的优先级获取
     * @param string $name
     * @param null $default
     * @param array $filter
     * @return array|mixed|void|null
     */
    public function getParam($name = '', $default = null, $filter = []) {
        $getData = $this->query->all();
        $postData = $this->request->all();
        $pathData = $this->attributes->all();

        $data = array_merge($pathData, $getData, $postData);
        return $this->input($data, $name, $default, $filter);
    }

    /**
     * 获取头信息
     * @param  $name
     * @param null $default
     * @return mixed
     */
    public function getHeader($name = '', $default = null) {
        $name = $name == '' ? null : $name;
        return $this->retrieveItem('headers', $name, $default);
    }

    /**
     * 获取服务器信息
     * @param $name
     * @param null $default
     * @return mixed
     */
    public function getServer($name = '', $default = null) {
        $name = $name == '' ? null : $name;
        return $this->retrieveItem('server', $name, $default);
    }

    /**
     * 判断cookie信息是否存在
     * @param $name
     * @return bool
     */
    public function hasCookie($name) {
        return !is_null($this->getCookie($name));
    }

    /**
     * 获取cookie信息内容
     * @param $name
     * @param null $default
     * @return Cookie
     */
    public function getCookie($name = '', $default = null) {
        $name = $name == '' ? null : $name;
        return $this->retrieveItem('cookies', $name, $default);
    }

    /**
     * 获取上传文件
     * @param $name
     * @return UploadedFile
     */
    public function getFile($name = '') {
        $name = $name == '' ? null : $name;
        return $this->retrieveItem('files', $name, null);
    }

    /**
     * server、file等参数处理
     * @param $source
     * @param string $key
     * @param null $default
     * @return mixed
     */
    protected function retrieveItem($source, $key = '', $default = null) {
        if (is_null($key)) {
            return $this->$source->all();
        }
        return $this->$source->get($key, $default);
    }

    /**
     * 从给定的数组中获取变量 支持过滤机默认值
     * @param array $data
     * @param false|string $name false 获取原始未处理数据  empty 获取完整数据
     * @param mixed $default
     * @param array $filter
     * @return array|mixed|void|null
     */
    public function input($data = [], $name = '', $default = null, $filter = []) {
        if (false === $name) {
            return $data;
        }

        $name = strval($name);
        if ('' != $name) {
            if (false !== strpos($name, '/')) {
                list($name, $valueType) = explode('/', $name);
            }

            $data = $this->getData($data, $name);

            if (is_null($data)) {
                return $default;
            }
            if (is_object($data)) {
                return $data;
            }
        }

        //使用过滤器进行数据处理
        $filter = $this->getFilter($filter);

        $this->filterValue($data, $filter, $default);

        if (isset($valueType) && !empty($valueType)) {
            $this->typeCast($data, $valueType);
        }

        return $data;
    }

    /**
     * 设置当前的全局过滤规则
     * @access public
     * @param array $filter 过滤规则
     * @return void
     */
    public function setFilter(array $filter) {
        $this->filter = $filter;
    }

    /**
     * 获取数据过滤规则，指定了外部过滤规则，那么全局默认过滤规则就不会被使用,所以如果是想在全局基础上添加过滤规则，需要把全局规则也带上
     * @param null|array|'' $filter    null 禁用所有过滤器  array 使用传入的过滤器  '' 使用默认全局过滤器
     * @return array
     */
    protected function getFilter($filter) {
        if (is_null($filter)) {
            //如果传入的过滤器是null，表示对这个数据进行过滤，包括全局过滤器全部禁用
            $filter = [];
        } else {
            $filter = $filter ?: $this->filter;
            //过滤器只能使用数组形式传入,不支持字符串类型
            $filter = (array)$filter;
        }

        return $filter;
    }

    /**
     * 递归过滤给定的值
     * @access public
     * @param mixed $value 键值
     * @param array $filters 过滤方法+默认值
     * @param mixed $default 未设置时的默认值
     * @return mixed
     */
    public function filterValue(&$value, $filters, $default) {
        if (is_array($value)) {
            foreach ($value as &$item) {
                $this->filterValue($item, $filters, $value);
            }
        } else {
            if (empty($filters)) {
                return is_null($value) ? $default : $value;
            }
            foreach ($filters as $filter) {
                if (is_callable($filter)) {
                    // 调用函数或者方法过滤
                    $value = call_user_func($filter, $value);
                } elseif (is_scalar($value)) {
                    if (false !== strpos($filter, '/')) {
                        // 正则过滤
                        if (!preg_match($filter, $value)) {
                            // 匹配不成功返回默认值
                            $value = $default;
                            break;
                        }
                    } elseif (!empty($filter)) {
                        // filter函数不存在时, 则使用filter_var进行过滤
                        // filter为非整形值时, 调用filter_id取得过滤id
                        $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
                        if (false === $value) {
                            $value = $default;
                            break;
                        }
                    }
                }
            }
            return $value;
        }
    }

    /**
     * 从数组中获取数据
     * @param array $data
     * @param $name
     * @return array|mixed|void
     */
    protected function getData(array $data, $name) {
        if (empty($name)) {
            return $data;
        }
        $data = array_change_key_case($data, CASE_UPPER);
        $name = strtoupper($name);
        $nameArr = explode('.', $name);
        foreach ($nameArr as $item) {
            if (isset($data[$item])) {
                $data = $data[$item];
            } else {
                return;
            }
        }
        return $data;
    }

    /**
     * 强制类型转换
     * @access public
     * @param string $data
     * @param string $type
     * @return mixed
     */
    protected function typeCast(&$data, $type) {
        if (!in_array(strtolower($type), ['d', 'a', 'f', 'b', 's'])) {
            return;
        }
        if (is_array($data)) {
            foreach ($data as &$item) {
                $this->typeCast($item, $type);
            }
        } else {
            switch (strtolower($type)) {
                // 数组
                case 'a':
                    $data = (array)$data;
                    break;
                // 数字
                case 'd':
                    $data = (int)$data;
                    break;
                // 浮点
                case 'f':
                    $data = (float)$data;
                    break;
                // 布尔
                case 'b':
                    $data = (boolean)$data;
                    break;
                // 字符串
                case 's':
                    if (is_scalar($data)) {
                        $data = (string)$data;
                    } else {
                        throw new InvalidArgumentException('variable type error：' . gettype($data));
                    }
                    break;
            }
        }
    }
}